using NLog;

namespace graver.CodeAnalysis.Syntax;

/// <summary>
/// 词法解析
/// </summary>
internal class Lexer
{
    private static readonly Logger log = LogManager.GetCurrentClassLogger();

    /// <summary>
    /// 当前源码字符串
    /// </summary>
    private readonly string _text;

    /// <summary>
    /// 当前解析的位置
    /// </summary>
    private int _position;

    /// <summary>
    /// 诊断信息
    /// </summary>
    private readonly List<string> _diagnostics;

    public Lexer(string text)
    {
        _text = text;
        _position = 0;
        _diagnostics = new List<string>();
    }

    public IEnumerable<string> Diagnostics => _diagnostics;

    /// <summary>
    /// 当前解析的字符
    /// </summary>
    /// <value></value>
    private char CurrentCharacter => Peek(0);

    /// <summary>
    /// 下一个字符
    /// </summary>
    private char LookaheadCharacter => Peek(1);

    private char Peek(int offset)
    {
        var index = _position + offset;
        if (index >= _text.Length)
        {
            return '\0';
        }

        return _text[index];
    }

    /// <summary>
    /// 移动到下一个解析位置
    /// </summary>
    private void Next()
    {
        _position++;
    }

    /// <summary>
    /// 获取下一个 Token
    /// </summary>
    /// <returns>解析的 Token</returns>
    public SyntaxToken Lex()
    {
        /* 文件尾 */
        if (_position >= _text.Length)
        {
            return new SyntaxToken(SyntaxKind.EndOfFileToken, _position, null, null);
        }

        /* 解析数字 */
        if (char.IsDigit(CurrentCharacter))
        {
            var start = _position;
            while (char.IsDigit(CurrentCharacter))
            {
                Next();
            }

            var digitLength = _position - start;
            var digitText = _text.Substring(start, digitLength);
            if (int.TryParse(digitText, out var digitValue))
            {
                return new SyntaxToken(SyntaxKind.NumberToken, start, digitText, digitValue);
            }
            else
            {
                _diagnostics.Add($"The number {_text} isn't valid Int32");
                return new SyntaxToken(SyntaxKind.BadToken, start, digitText, digitText);
            }
        }

        /* 解析标识符 */
        if (char.IsLetter(CurrentCharacter) || '_' == CurrentCharacter)
        {
            var start = _position;
            while (char.IsLetter(CurrentCharacter) || '_' == CurrentCharacter || char.IsDigit(CurrentCharacter))
            {
                Next();
            }

            var identifierLength = _position - start;
            var identifierText = _text.Substring(start, identifierLength);
            var kind = SyntaxFacts.GetIdentifierKind(identifierText);
            return new SyntaxToken(kind, start, identifierText, null);
        }

        /* 解析空白字符 */
        if (char.IsWhiteSpace(CurrentCharacter))
        {
            var start = _position;
            while (char.IsWhiteSpace(CurrentCharacter))
            {
                Next();
            }

            var spaceLength = _position - start;
            var spaceText = _text.Substring(start, spaceLength);
            return new SyntaxToken(SyntaxKind.SpaceToken, start, spaceText, null);
        }

        /* 解析操作符 */
        switch (CurrentCharacter)
        {
            case '+':
                return new SyntaxToken(SyntaxKind.PlusToken, _position++, "+", null);
            case '-':
                return new SyntaxToken(SyntaxKind.MinusToken, _position++, "-", null);
            case '*':
                return new SyntaxToken(SyntaxKind.StarToken, _position++, "*", null);
            case '/':
                return new SyntaxToken(SyntaxKind.SlashToken, _position++, "/", null);
            case '(':
                return new SyntaxToken(SyntaxKind.OpenParenthesisToken, _position++, "(", null);
            case ')':
                return new SyntaxToken(SyntaxKind.CloseParenthesisToken, _position++, ")", null);
            case '!':
            {
                if ('=' == LookaheadCharacter)
                {
                    return new SyntaxToken(SyntaxKind.BangEqualsToken, _position += 2, "!=", null);
                }

                return new SyntaxToken(SyntaxKind.BangToken, _position++, "!", null);
            }

            case '&':
            {
                if ('&' == LookaheadCharacter)
                {
                    return new SyntaxToken(SyntaxKind.AmpersandAmpersandToken, _position += 2, "&&", null);
                }

                break;
            }
            case '|':
            {
                if ('|' == LookaheadCharacter)
                {
                    return new SyntaxToken(SyntaxKind.PipePipeToken, _position += 2, "||", null);
                }

                break;
            }
            case '=':
            {
                if ('=' == LookaheadCharacter)
                {
                    return new SyntaxToken(SyntaxKind.EqualsEqualsToken, _position += 2, "==", null);
                }

                break;
            }
        }

        _diagnostics.Add($"Error: bad character input: '{CurrentCharacter}'");
        return new SyntaxToken(SyntaxKind.BadToken, _position++, _text.Substring(_position - 1, 1), null);
    }
}
